unit SwinGameAPI;

interface
	const
		//Preset colours (backwards WinGraph style (BGR))
		ColourBlue = 16711680; 		//FF0000
		ColourGreen = 65280; 		//00FF00
		ColourRed = 255; 			//0000FF
		ColourWhite = 16777215; 	//FFFFFF
		ColourBlack = 0; 			//000000
		ColourYellow = 65535;		//00FFFF
		ColourPink = 16711935;		//FF00FF
		ColourTurquoise = 16776960;	//FFFF00
		ColourGrey = 8355711;		//7F7F7F
		NotTransparent = 16777216;	//1 larger than the largest number (since longword is 32bit and FFFFFF is 24-bit, we've got space)
		
		//Keyboard key constants
		LeftArrowKey  = #75;
		RightArrowKey = #77;
		UpArrowKey    = #72;
		DownArrowKey  = #80;
		
		//Collision Detection Ranges
		CollisionRangeEquals      = 0;
		CollisionRangeGreaterThan = 1;
		CollisionRangeLessThan    = 2;
		
		//Precreated Fonts
		CourierNewFont 		= word(0);
		SystemFont 			= word(1);
		MSSansSerifFont 	= word(2);
		TimesNewRomanFont 	= word(3);
		ArialFont 			= word(4);
		LucidaConsoleFont 	= word(5);
		
		//Font Styles (logical OR them together to combine)
		ItalicFont 		= word($1000);
		UnderlineFont 	= word($0100);
		BoldFont 		= word($0010);
		

	type
		Colour = longword;
		
		Font = Integer;
		
		Vector = record
			x,y : Single;
		end;
		
		Bitmap = record
			btmp : Integer;
			transparentColour : Colour;
			freed : Boolean;
			width : Integer;
			height : Integer;
			imageHasAlpha : Boolean;
			alphaAmount : Integer;
		end;
		
		Sprite = record
			bitmaps : Array of ^Bitmap;
			xPos : Single;
			yPos : Single;
			currentFrame : Integer;
		end;
		
		DecayProperties = record 
			posXDecay : Single; //The amount to decay the X component towards zero if the X component is positive
			negXDecay : Single; //The amount to decay the X component towards zero if the X component is negative
			posYDecay : Single; //The amount to decay the Y component towards zero if the Y component is positive
			negYDecay : Single; //The amount to decay the Y component towards zero if the Y component is negative
		end;
		
		SoundBuffer = Cardinal;
		
		SoundSource = record 
			pBuffer : ^SoundBuffer;
			source : Cardinal;
			sourcepos: array [0..2] of Single;   
			sourcevel: array [0..2] of Single; 
			listenerpos: array [0..2] of Single; 
			listenervel: array [0..2] of Single; 
			listenerori: array [0..5] of Single; 
		end; 

	//Graphics Window functions
	procedure OpenGraphicsWindow(caption : String; width : Integer; height : Integer); overload;
	procedure OpenGraphicsWindow(caption : String); overload;
	procedure CloseGraphicsWindow();
	procedure ClearScreen(toColour : Colour);
	procedure ClearScreen();
	procedure RefreshScreen();
	function  WindowCloseRequested(): Boolean;
	
	//Bitmap Functions
	function  LoadBitmap(pathToBitmap : String; width, height : Integer; bitmapHasAlpha : Boolean; alphaAmount : Integer; transparentColour : Colour): Bitmap;
	procedure FreeBitmap(var bitmapToFree : Bitmap);
	
	//Sprite Functions
	function  CreateSprite(const startBitmap : Bitmap): Sprite;
	procedure FreeSprite(var spriteToFree : Sprite);
	procedure AddBitmapToSprite(var spriteToAddTo : Sprite; const bitmapToAdd : Bitmap);
	procedure DrawSprite(const spriteToDraw : Sprite); overload;
	procedure DrawSprite(const spriteToDraw : Sprite; vwPrtX, vwPrtY, vwPrtWidth, vwPrtHeight : Integer); overload;
	procedure MoveSprite(var spriteToMove : Sprite; movementVector : Vector);
	procedure MoveSpriteTo(var spriteToMove : Sprite; x,y : Integer);
	function  IsSpriteOffscreen(const theSprite : Sprite; vwPrtX, vwPrtY, vwPrtWidth, vwPrtHeight : Integer): Boolean;
	
	//Sprite Collision Functions
	function  HasSpriteCollidedX(const theSprite : Sprite; x : Integer; range : Integer): Boolean;
	function  HasSpriteCollidedY(const theSprite : Sprite; y : Integer; range : Integer): Boolean;
	function  HasSpriteCollidedWithRect(const theSprite : Sprite; x, y : Single; width, height : Integer): Boolean;
	function  HaveSpritesCollided(const sprite1, sprite2 : Sprite): Boolean;
	
	//Vector Functions
	function  CreateVector(x,y : Single; invertY : boolean): Vector; overload;
	function  CreateVector(x,y : Single): Vector; overload;
	function  AddVectors(v1, v2 : Vector): Vector;
	function  SubtractVectors(v1, v2 : Vector): Vector;
	function  InvertVector(v : Vector): Vector;
	function  ChopVector(theVector : Vector; minX, maxX, minY, maxY : Integer): Vector;
	function  GetUnitVector(theVector : Vector): Vector;
	function  IsZeroVector(theVector : Vector): Boolean;
	function  GetVectorMagnitude(theVector : Vector): Single;
	function  DecayVector(theVector : Vector; const decayProps : DecayProperties): Vector;
	
	//Colour functions
	function  GetColour(red, green, blue : Integer) : Colour;
	
	//Text drawing functions
	function  CreateFont(fontName : String): Font;
	procedure DrawText(theText : String; textColour : Colour; theFont : Font; fontStyle, fontSize, textAngle, xPos, yPos : Integer);
	
	//Primitive drawing function
	procedure DrawRectangle(theColour : Colour; filled : Boolean; xPos, yPos, width, height : Integer);
	
	//Keyboard Input functions
	function  AreKeysWaiting(): Boolean;
	function  GetKeyPress(): Char;
	function  IsKeyPressed(virtKeyCode : Byte): Boolean;
	
	//Sound Functions
	function  LoadWAVIntoBuffer(filename : String): SoundBuffer;
	procedure UnloadBuffer(var buffer : SoundBuffer);
	function  CreateSoundSource(const buffer : SoundBuffer; loopSound : Boolean; volume : Single): SoundSource;
	procedure DeleteSoundSource(var source : SoundSource);
	procedure PlaySoundSource(const source : SoundSource);
	procedure PauseSoundSource(const source : SoundSource);
	procedure StopSoundSource(const source : SoundSource);
	function  IsSoundSourcePlaying(const source : SoundSource): Boolean;
	function  IsSoundSourcePaused(const source : SoundSource): Boolean;
	function  IsSoundSourceStopped(const source : SoundSource): Boolean;
	
	//Other functions
	procedure Sleep(time : Integer);
	function  GetPerformanceCounter(): Int64;

implementation
	uses Windows, WinGraph, SysUtils, WinCRT, OpenAL;

	var
		//The performance counter frequency (set during unit initialisation)
		perfCounterFreq : Int64;
		
		
	
	//Opens a Graphics Window at a certain height and width (in pixels)
	//Params:     - caption: The caption of the window
	//            - width: the width of the window in pixels
	//            - height: the height of the window in pixels
	//Exceptions: - If the graphics window fails to open
	procedure OpenGraphicsWindow(caption : String; width : Integer; height : Integer); overload;
	var
		colourDepth, resolution, errorCode : SmallInt;
		errorCodeStr : String;
	begin
		SetWindowSize(width, height);
	
		//Open the Graphics Window
		colourDepth := NoPalette;
		resolution  := mCustom;
		InitGraph(colourDepth, resolution, caption);
		
		//Check for errors
		errorCode := GraphResult();
		if errorCode <> grOK then
		begin
			Str(errorCode, errorCodeStr);
			raise Exception.Create('Error when initialising the graphics window! Code: ' + errorCodeStr + '. Message: ' + GraphErrorMsg(errorCode));
		end;
		
		//Turn off auto painting
		UpdateGraph(UpdateOff);
	end;
	
	
	//Opens a Graphics Window with 800x600 size
	//Params:     - caption: The caption of the window
	//Exceptions: - If the graphics window fails to open
	procedure OpenGraphicsWindow(caption : String); overload;
	begin
		OpenGraphicsWindow(caption, 800,600);
	end;
	
	
	//Closes the open graphics window
	procedure CloseGraphicsWindow();
	begin
		CloseGraph();
	end;
	
	
	//Clears the screen, replacing it with a colour
	//Params:     - toColour: the colour to fill with
	procedure ClearScreen(toColour : Colour); overload;
	begin
		SetBkColor(toColour);
		ClearViewPort();
	end;
	
	//Clears the screen, replacing it with black
	procedure ClearScreen(); overload;
	begin
		ClearScreen(GetColour(0,0,0));
	end;
	
	
	//Tells the screen to show updated content now
	procedure RefreshScreen();
	begin
		UpdateGraph(UpdateNow);
	end;
	
	
	//Gets a Colour variable that represents a colour
	//Params:     - red: Intensity in red (0-255)
	//            - green: Intensity in green (0-255)
	//            - blue: Intensity in blue (0-255)
	//Returns:    The Colour
	function GetColour(red, green, blue : Integer) : Colour;
	begin
		//Note: This is in WinGraph colour format: BGR (its freaking backwards!!)
		blue := blue shl 16;
		green := green shl 8;
		
		result := blue or green or red;
	end;
	
	
	//Reverse the colour gotten from the GetColour function to make it
	//non-WinGraphy (not freaking backwards).
	function ReverseColourByteOrder(theColour : Colour) : Colour;
	var
		red,green,blue : Integer;
	begin
		blue := (theColour and 16711680) shr 16; //16711680 = 0xFF0000
		green := (theColour and 65280); //65280 = 0x00FF00
		red := (theColour and 255) shl 16; //255 = 0x0000FF
		
		result := red or green or blue;
	end;
	
	
	//Loads a bitmap into memory
	//Params:     - pathToBitmap: The path of the bitmap to load
	//            - width: The width in pixels of the bitmap
	//            - height: The height in pixels of the bitmap
	//            - bitmapHasAlpha: Does the bitmap have alpha transparency
	//            - alphaAmount: How much artificial alpha to apply to the image
	//            - tranparentColour: The colour to replace with transparency.
	//              If no transparency is wanted specify NotTransparent
	//Returns:    The Bitmap
	function  LoadBitmap(pathToBitmap : String; width, height : Integer; bitmapHasAlpha : Boolean; alphaAmount : Integer; transparentColour : Colour): Bitmap;
	begin
		result.btmp := LoadImage(PChar(pathToBitmap));
		result.transparentColour := transparentColour;
		result.freed := false;
		result.height := height;
		result.width := width;
		result.imageHasAlpha := bitmapHasAlpha;
		result.alphaAmount := alphaAmount;
	end;
	
	
	//Frees a bitmap from memory
	//Params:     - bitmapToFree: The bitmap to free
	procedure FreeBitmap(var bitmapToFree : Bitmap);
	begin
		if bitmapToFree.freed = false then
			FreeImage(bitmapToFree.btmp);
			
		bitmapToFree.freed := true;
	end;
	
	
	//Creates a Sprite
	//Params:     - startBitmap: The initial bitmap the sprite has
	//Returns:    The Sprite created
	function CreateSprite(const startBitmap : Bitmap): Sprite;
	begin
		SetLength(result.bitmaps, 1);
		
		result.bitmaps[0]            := @startBitmap;
		result.xPos                  := 0;
		result.yPos                  := 0;
		result.currentFrame          := 0;
	end;
	
	
	//Frees a sprite's bitmaps from memory
	//Params:     - The sprite whose bitmaps will be freed
	procedure FreeSprite(var spriteToFree : Sprite);
	var
		index : Integer;
	begin
		for index := 0 to High(spriteToFree.bitmaps) do
		begin
			FreeBitmap(spriteToFree.bitmaps[index]^);
		end;
		
		SetLength(spriteToFree.bitmaps, 0);
	end;
	
	
	//Adds another bitmap to a sprite (usually used for an animation)
	//Params:     - spriteToAddTo: The sprite to which to add the new bitmap
	//            - bitmapToAdd: The bitmap to add
	procedure AddBitmapToSprite(var spriteToAddTo : Sprite; const bitmapToAdd : Bitmap);
	begin
		//Resize the array
		SetLength(spriteToAddTo.bitmaps, Length(spriteToAddTo.bitmaps) + 1);
		
		//Add the values to the array
		spriteToAddTo.bitmaps[High(spriteToAddTo.bitmaps)] := @bitmapToAdd;
	end;
	
	
	//Draws a sprite on the screen taking into account the world
	//coordinates of the viewport
	//This assumes the coordinates in the sprite are world coordinates
	//Params:     - spriteToDraw: The sprite to draw
	//            - vwPrtX: The x world coordinate of the viewport
	//            - vwPrtY: The y world coordinate of the viewport
	//            - vwPrtWidth: The width of the viewport
	//            - vwPrtHeight: The height of the viewport
	procedure DrawSprite(const spriteToDraw : Sprite; vwPrtX, vwPrtY, vwPrtWidth, vwPrtHeight : Integer); overload;
	var
		reversedColour : Colour;
	begin
		//Don't draw if its offscreen
		if IsSpriteOffscreen(spriteToDraw, vwPrtX, vwPrtY, vwPrtWidth, vwPrtHeight) and (vwPrtWidth <> 0) and (vwPrtHeight <> 0) then exit;
		
		if spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.imageHasAlpha then
		begin
			DrawAlphaImage(Trunc(spriteToDraw.xPos) - vwPrtX, Trunc(spriteToDraw.yPos) - vwPrtY, spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.btmp, spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.alphaAmount);
		end
		else if spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.transparentColour = NotTransparent then
		begin
			DrawImage(Trunc(spriteToDraw.xPos) - vwPrtX, Trunc(spriteToDraw.yPos) - vwPrtY, spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.btmp, copyPut);
		end
		else
		begin
			//Reverse the colour from stupid backwards WinGraph format to real RGB
			reversedColour := ReverseColourByteOrder(spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.transparentColour);
			
			//DrawTransparentImage(spriteToDraw.xPos - vwPrtX, spriteToDraw.yPos - vwPrtY, spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.btmp, reversedColour);
			DrawTransparentImage(Trunc(spriteToDraw.xPos) - vwPrtX, Trunc(spriteToDraw.yPos) - vwPrtY, spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.btmp, spriteToDraw.bitmaps[spriteToDraw.currentFrame]^.transparentColour);
		end;
	end;
	
	
	//Draws a sprite on the screen
	//Params:     - spriteToDraw: The sprite to draw
	procedure DrawSprite(const spriteToDraw : Sprite); overload;
	begin
		DrawSprite(spriteToDraw, 0, 0, 0, 0);
	end;
	
	
	
	//Calculates whether a sprite is outside of the viewport area
	//and hence that it is useless to draw it since it won't be seen
	//Params:     - theSprite: The sprite to check
	//            - vwPrtX: The x world coordinate of the viewport
	//            - vwPrtY: The y world coordinate of the viewport
	//            - vwPrtWidth: The width of the viewport
	//            - vwPrtHeight: The height of the viewport
	//Returns:    True if offscreen, false otherwise
	function IsSpriteOffscreen(const theSprite : Sprite; vwPrtX, vwPrtY, vwPrtWidth, vwPrtHeight : Integer): Boolean;
	begin
		if theSprite.xPos > vwPrtX + vwPrtWidth then result := true
		else if theSprite.xPos + theSprite.bitmaps[theSprite.currentFrame].width < vwPrtX then result := true
		else if theSprite.yPos > vwPrtY + vwPrtHeight then result := true
		else if theSprite.yPos + theSprite.bitmaps[theSprite.currentFrame].height < vwPrtY then result := true
		else result := false;
	end;
	
	
	//Moves a sprite from its current position in direction determined 
	//by a vector
	//Params:     - spriteToMove: The sprite to move
	//            - movementVector: The vector to move the sprite in
	procedure MoveSprite(var spriteToMove : Sprite; movementVector : Vector);
	begin
		spriteToMove.xPos := spriteToMove.xPos + movementVector.x;
		spriteToMove.yPos := spriteToMove.yPos + movementVector.y;
	end;
	
	
	//Moves a sprite to an absolute position
	//Params:     - spriteToMove: The sprite to move
	//            - x: The x coordinate to move to
	//            - y: The y coordinate to move to
	procedure MoveSpriteTo(var spriteToMove : Sprite; x,y : Integer);
	begin
		spriteToMove.xPos := x;
		spriteToMove.yPos := y;
	end;
	
	
	//Creates a vector
	//Params:     - x: The x component of the vector
	//            - y: The y component of the vector
	//            - invertY: If you want to automatically invert the y component
	//              of the vector. You would do this because the origin for the
	//              graphics window is in the top left corner, hence moving a 
	//              sprite +5 in the y direction actually moves the sprite DOWN!
	//              This can be counter-intuitive, so setting this to true will
	//              reverse the y direction making +5 in y move the sprite UP
	//Returns:    The Vector
	function CreateVector(x,y : Single; invertY : boolean): Vector; overload;
	begin
		if invertY then y := y * -1;
		
		result.x := x;
		result.y := y;
	end;
	
	
	//Creates a vector
	//Params:     - x: The x component of the vector
	//            - y: The y component of the vector
	//Returns:    The Vector
	function CreateVector(x,y : Single): Vector; overload;
	begin
		result := CreateVector(x,y,false);
	end;
	
	
	//Adds two vectors to get the resultant vector
	//Params:     - v1: The first vector
	//            - v2: The second vector, which is added to the first
	//Returns:    The resultant Vector
	function AddVectors(v1, v2 : Vector): Vector;
	begin
		result.x := v1.x + v2.x;
		result.y := v1.y + v2.y;
	end;
	
	
	
	//Subtracts two vectors to get a resultant vector
	//Params:     - v1: The first vector
	//            - v2: The second vector which is subtracted from the first
	//Returns:   The resultant Vector
	function SubtractVectors(v1, v2 : Vector): Vector;
	begin
		result.x := v1.x - v2.x;
		result.y := v1.y - v2.y;
	end;
	
	
	//Inverts a vector (makes it go in its opposite direction)
	//Params:     - v: The Vector to invert
	//Returns:      The inverted Vector
	function InvertVector(v : Vector): Vector;
	begin
		result.x := v.x * -1;
		result.y := v.y * -1;
	end;
	
	
	//If the input Vector is bigger than the limits given, it is
	//chopped off at the limits
	//Params:     - theVector: The vector to chop (if necessary)
	//            - minX: The minimum value that the x component of
	//              the vector can be
	//            - maxX: The maxiumum value that the x component of
	//              the vector can be
	//            - minY: The minimum value that the y component of
	//              the vector can be
	//            - maxY: The maxiumum value that the y component of
	//              the vector can be
	function ChopVector(theVector : Vector; minX, maxX, minY, maxY : Integer): Vector;
	begin
		if theVector.x > maxX then theVector.x := maxX;
		if theVector.x < minX then theVector.x := minX;
		if theVector.y > maxY then theVector.y := maxY;
		if theVector.y < minY then theVector.y := minY;
		
		result := theVector;
	end;
	
	
	//Gets the unit vector (vector with a magnitude of 1) of the
	//input vector
	//Params:     - theVector: The vector to create a unit vector of
	//Returns:    The unit Vector
	function GetUnitVector(theVector : Vector): Vector;
	var
		temp : Double;
		vectorMagnitude : Single;
	begin
		vectorMagnitude := GetVectorMagnitude(theVector);
		
		if vectorMagnitude = 0 then
			temp := 0
		else
			temp := 1 / GetVectorMagnitude(theVector);
		
		result.x := temp * theVector.x;
		result.y := temp * theVector.y;
	end;
	
	
	
	//Checks to see whether a vector is zero or not
	//Params:     - theVector: The Vector to check
	//Returns:    True if it is zero, false otherwise
	function IsZeroVector(theVector : Vector): Boolean;
	begin
		result := (theVector.x = 0) and (theVector.y = 0);
	end;
	
	
	//Gets the magnitude of a Vector
	//Params:     - theVector: The Vector to get the magnitude of
	//Returns:    The Vector's magnitude
	function GetVectorMagnitude(theVector : Vector): Single;
	begin
		result := Sqrt((theVector.x * theVector.x) + (theVector.y * theVector.y));
	end;
	
	
	//Decays a vector towards zero by a certain amount
	//Params:     - theVector: The Vector to decay
	//            - decayProps: The DecayProperties to decay with (see DecayProperties
	//              doc for more info)
	//Returns:    The decayed Vector
	function DecayVector(theVector : Vector; const decayProps : DecayProperties): Vector;
	begin
		if theVector.x > 0 then
		begin
			theVector.x := theVector.x - decayProps.posXDecay;
			if theVector.x < 0 then theVector.x := 0;
		end
		else if theVector.x < 0 then
		begin
			theVector.x := theVector.x + decayProps.negXDecay;
			if theVector.x > 0 then theVector.x := 0;
		end;
		
		if theVector.y > 0 then
		begin
			theVector.y := theVector.y - decayProps.posYDecay;
			if theVector.y < 0 then theVector.y := 0;
		end
		else if theVector.y < 0 then
		begin
			theVector.y := theVector.y + decayProps.negYDecay;
			if theVector.y > 0 then theVector.y := 0;
		end;
		
		result.x := theVector.x;
		result.y := theVector.y;
	end;
	
	
	//Checks to see whether a sprite is touching an X position
	//eg Draw the line for the equation x = <your number> and if the
	//sprite is touching that, then this function will return true.
	//Params:     - theSprite: The sprite to check
	//            - x: The x position
	//            - range: Collision check range. One of CollisionRangeEquals
	//              (collided with x = ?), CollisionRangeGreaterThan (collided
	//              with x > ?) or CollisionRangeLessThan (collided with x < ?)
	//Returns:    True if it is colliding, false otherwise
	function HasSpriteCollidedX(const theSprite : Sprite; x : Integer; range : Integer): Boolean;
	begin
		if range = CollisionRangeEquals then
			result := (x >= theSprite.xPos) and (x <= theSprite.xPos + theSprite.bitmaps[theSprite.currentFrame].width)
		else if range = CollisionRangeGreaterThan then
			result := x <= theSprite.xPos + theSprite.bitmaps[theSprite.currentFrame].width
		else if range = CollisionRangeLessThan then
			result := x >= theSprite.xPos
		else
			raise Exception.Create('Invalid Collision Range');
	end;
	
	
	//Checks to see whether a sprite is touching a Y position
	//eg Draw the line for the equation y = <your number> and if the
	//sprite is touching that, then this function will return true.
	//Params:     - theSprite: The sprite to check
	//            - y: The y position
	//            - range: Collision check range. One of CollisionRangeEquals
	//              (collided with y = ?), CollisionRangeGreaterThan (collided
	//              with y > ?) or CollisionRangeLessThan (collided with y < ?)
	//Returns:    True if it is colliding, false otherwise
	function HasSpriteCollidedY(const theSprite : Sprite; y : Integer; range : Integer): Boolean;
	begin
		if range = CollisionRangeEquals then
			result := (y >= theSprite.yPos) and (y <= theSprite.yPos + theSprite.bitmaps[theSprite.currentFrame].height)
		else if range = CollisionRangeGreaterThan then
			result := y <= theSprite.yPos + theSprite.bitmaps[theSprite.currentFrame].height
		else if range = CollisionRangeLessThan then
			result := y >= theSprite.yPos
		else
			raise Exception.Create('Invalid Collision Range');
	end;
	
	
	//Checks to see whether a sprite is touching a rectangular area
	//Params:     - theSprite: The sprite to check
	//            - x: The x coordinate of the top left corner of 
	//              the rectangular area
	//            - y: The y coordinate of the top left corner of 
	//              the rectangular area
	//            - width: The width of the rectangular area
	//            - height: The height of the rectangular area
	function HasSpriteCollidedWithRect(const theSprite : Sprite; x, y : Single; width, height : Integer): Boolean;
	begin
		if theSprite.yPos + theSprite.bitmaps[theSprite.currentFrame].height <= y then result := false
		else if theSprite.yPos >= y + height then result := false
		else if theSprite.xPos + theSprite.bitmaps[theSprite.currentFrame].width <= x then result := false
		else if theSprite.xPos >= x + width then result := false
		else result := true;
	end;
	
	
	//Checks to see whether two sprites are touching
	//Params:     - sprite1: The first sprite
	//            - sprite2: The second sprite
	//Returns:    True if they are touching, false otherwise
	function HaveSpritesCollided(const sprite1, sprite2 : Sprite): Boolean;
	begin
		result := HasSpriteCollidedWithRect(sprite1, sprite2.xPos, sprite2.yPos, sprite2.bitmaps[sprite2.currentFrame].width, sprite2.bitmaps[sprite2.currentFrame].height);
	end;
	
	
	//Checks to see whether there are any keys waiting on
	//the input queue
	//Returns:    True if there are, false otherwise
	function AreKeysWaiting(): Boolean;
	begin
		result := KeyPressed();
	end;
	
	
	//Gets a key press off the queue
	//Returns:    The character of the key that was pressed
	function GetKeyPress(): Char;
	begin
		result := ReadKey();
	end;
	
	
	//Checks to see whether a key is pressed or not.
	//Params:     - virtKeyCode: The Virtual-Key Code of the 
	//              key you want to check is pressed. See
	//              http://msdn2.microsoft.com/en-us/library/ms645540.aspx
	//              for the list.
	//Returns:    True if the key is pressed, false otherwise
	function IsKeyPressed(virtKeyCode : Byte): Boolean;
	begin
		result := IsKeyDown(virtKeyCode);
	end;
	
	
	//Sleep the thread
	//Params:     - time: How many milliseconds to sleep for
	procedure Sleep(time : Integer);
	begin
		Delay(time);
	end;
	
	
	//Has the window had a close request (eg the X button was clicked)
	//If it has, you should close the window yourself
	//Returns:    True if it has, false if it hasn't
	function WindowCloseRequested(): Boolean;
	begin
		result := WndCloseRequested();
	end;

	
	//Gets a system timestamp in microseconds. Calculate the difference between
	//two calls of this to get time elapsed (in microseconds).
	//Returns:    64-bit Integer timestamp (1 = 1 microsecond)
	function GetPerformanceCounter(): Int64;
	var
		counter : Int64;
	begin		
		if QueryPerformanceCounter(@counter) = false then
			raise Exception.Create('Performance Counter not supported on this machine.');
		
		result := counter div (perfCounterFreq div 1000000); //Returns in microseconds
	end;
	
	
	//Creates a font for use. 
	//NB. Do not create the one font more than once. Save it globally and keep it.
	//Some fonts are already created. See Precreated Fonts section.
	//Params:     -	fontName: The name of the font you want to create
	//Returns:    The font
	function CreateFont(fontName : String): Font;
	begin
		result := InstallUserFont(fontName);
	end;
	
	
	//Draws some text to the screen
	//Params:     - theText: the text to draw
	//            - textColour: the colour of the text
	//            - theFont: The font of the text
	//            - fontStyle: The font style (see Font Styles section)
	//            - fontSize: the size of the font in pixels
	//            - textAngle: the angle (in degrees) to draw the text at. 
	//              May not work with some fonts
	//            - xPos: the x coordinate at which the top left corner of 
	//              the text will be drawn
	//            - yPos: the y coordinate at which the top left corner of 
	//              the text will be drawn
	procedure DrawText(theText : String; textColour : Colour; theFont : Font; fontStyle, fontSize, textAngle, xPos, yPos : Integer);
	begin
		SetColor(textColour);
		SetTextStyle(theFont or fontStyle, textAngle, fontSize);
		OutTextXY(xPos, yPos, theText);
	end;
	
	
	//Draws a rectangle
	//Params:     - theColour: the colour of rectangle to draw
	//            - filled: Whether or not to fill the rectangle
	//            - xPos: the x coordinate at which the top left corner of 
	//              the rectangle will be drawn
	//            - yPos: the y coordinate at which the top left corner of 
	//              the rectangle will be drawn
	//            - width: the width of the rectangle
	//            - height: the height of the rectangle
	procedure DrawRectangle(theColour : Colour; filled : Boolean; xPos, yPos, width, height : Integer);
	begin
		SetColor(theColour);	
	
		if filled then
		begin
			SetFillStyle(SolidFill, theColour);
			FillRect(xPos, yPos, xPos + width, yPos + height);
		end
		else
		begin
			Rectangle(xPos, yPos, xPos + width, yPos + height);
		end;
	end;
	
	
	//Loads a wave (.wav) file into memory.
	//Params:     - filename: The filename of the .wav file
	//Returns:    The sound buffer created
	function LoadWAVIntoBuffer(filename : String): SoundBuffer;
	var
	    format: TALEnum;
	    size: TALSizei;
	    freq: TALSizei;
	    loop: TALInt;
	    data: TALVoid;
	begin
		//Load the sound buffer
	    AlGenBuffers(1, @result);
	    AlutLoadWavFile(filename, format, data, size, freq, loop);
	    AlBufferData(result, format, data, size, freq);
	    AlutUnloadWav(format, data, size, freq);
	end;
	
	
	//Deletes a sound buffer from memory.
	//Params:     - buffer: The buffer to delete
	procedure UnloadBuffer(var buffer : SoundBuffer);
	begin
		AlDeleteBuffers(1, @buffer);
	end;
	
	
	//Creates a a sound source (a thing that says how a buffer is played).
	//Params:     - buffer: The buffer the source will play
	//            - loopSound: Whether you want the sound to loop or not
	//            - volume: 1.0 is normal volume. 0.5 is half volume and so on
	//Returns:    The sound source created
	function CreateSoundSource(const buffer : SoundBuffer; loopSound : Boolean; volume : Single): SoundSource;
	begin
	    //Set the pointer to the buffer
	    result.pBuffer := @buffer;
	    
	    //Set Sound and listener position, velocity, and origin values
	    //to some simple settings (the centre of 3D space)
	    result.sourcepos[0] := 0.0;
	    result.sourcepos[1] := 0.0;
	    result.sourcepos[2] := 0.0;
	    result.sourcevel[0] := 0.0;
	    result.sourcevel[1] := 0.0;
	    result.sourcevel[2] := 0.0;
	    result.listenerpos[0] := 0.0;
	    result.listenerpos[1] := 0.0;
	    result.listenerpos[2] := 0.0;
	    result.listenervel[0] := 0.0;
	    result.listenervel[1] := 0.0;
	    result.listenervel[2] := 0.0;
	    result.listenerori[0] := 0.0;
	    result.listenerori[1] := 0.0;
	    result.listenerori[2] := -1.0;
	    result.listenerori[3] := 0.0;
	    result.listenerori[4] := 1.0;
	    result.listenerori[5] := 0.0;
	    
	    //Make the sound source
	    AlGenSources(1, @(result.source));
	    AlSourcei ( result.source, AL_BUFFER, result.pBuffer^);
	    AlSourcef ( result.source, AL_PITCH, 1.0 );
	    AlSourcef ( result.source, AL_GAIN, volume);
	    AlSourcefv ( result.source, AL_POSITION, @(result.sourcepos));
	    AlSourcefv ( result.source, AL_VELOCITY, @(result.sourcevel));
	    AlSourcei ( result.source, AL_LOOPING, Integer(loopSound));
	    //Specify where the sound is heard
	    AlListenerfv ( AL_POSITION, @(result.listenerpos));
	    AlListenerfv ( AL_VELOCITY, @(result.listenervel));
	    AlListenerfv ( AL_ORIENTATION, @(result.listenerori));
	end;
	
	
	//Deletes a sound source from memory
	//Params:     - source: The sound source to delete
	procedure DeleteSoundSource(var source : SoundSource);
	begin
		AlDeleteSources(1, @(source.source));
	end;
	
	
	//Plays a sound source
	//Params:     - source: The sound source to play
	procedure PlaySoundSource(const source : SoundSource);
	begin
		AlSourcePlay(source.source);
	end;
	
	
	//Pauses a playing sound source
	//Params:     - source: The sound source to pause
	procedure PauseSoundSource(const source : SoundSource);
	begin
		AlSourcePause(source.source);
	end;
	
	
	//Stops a playing sound source
	//Params:     - source: The sound source to stop
	procedure StopSoundSource(const source : SoundSource);
	begin
		AlSourceStop(source.source);
	end;
	
	
	//Checks to see whether the sound source is currently playing
	//Params:     - source: The sound source to check
	//Returns:    True if playing, false otherwise
	function IsSoundSourcePlaying(const source : SoundSource): Boolean;
	var 
		state : Integer;
	begin
		alGetSourcei(source.source, AL_SOURCE_STATE, @state);
		result := state = AL_PLAYING;
	end;
	
	
	//Checks to see whether the sound source is currently paused
	//Params:     - source: The sound source to check
	//Returns:    True if paused, false otherwise
	function IsSoundSourcePaused(const source : SoundSource): Boolean;
	var 
		state : Integer;
	begin
		alGetSourcei(source.source, AL_SOURCE_STATE, @state);
		result := state = AL_PAUSED;
	end;
	
	
	//Checks to see whether the sound source is currently stopped
	//Params:     - source: The sound source to check
	//Returns:    True if stopped, false otherwise
	function IsSoundSourceStopped(const source : SoundSource): Boolean;
	var 
		state : Integer;
	begin
		alGetSourcei(source.source, AL_SOURCE_STATE, @state);
		result := state = AL_STOPPED;
	end;
	
	
	//Not exposed. Initialises the SwinGameAPI unit.
	procedure Initialise();
	var
		argv: Array of PalByte;
	begin
		//Get the performance counter frequency (for GetPerformanceCounter() to use)
		if QueryPerformanceFrequency(@perfCounterFreq) = false then
			raise Exception.Create('Performance Counter not supported on this machine!');	
			
		//Initialise OpenAL
	    InitOpenAL();
	    AlutInit(nil,argv);
	end;


initialization	
begin
	Initialise();
end;

finalization
begin
	AlutExit();
end; 

end.
